package requests.math.programmerCalc;

import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

import requests.IRequest;

public class ProgrammerCalc extends JFrame implements IRequest, ActionListener, KeyListener {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int max, ptr, ptr2;
	private JPanel panel;
	private JLabel screen;
	private JButton actions[], values[];
	private Font buttonsFont = new Font("Times New Roman", 1, 20), screenFont = new Font("Arial", 1, 26);
	private GridBagLayout layout;
	private GridBagConstraints c;
	private char binary[], binary2[], operation = ' ';
	private String view = "bin", size = "dword";
	private BinaryOperations binaryOp = new BinaryOperations();
	private ArithmeticOperations arithmetic = new ArithmeticOperations();
	private Color buttonColor = new Color(200,200,200), screenColor = new Color(255,255,255), 
				  activeView = new Color(255,160,30), activeSize = new Color(255,90,50);
	
	public ProgrammerCalc(int width, int heigth) {
		
		setSize(width, heigth);
		setLocation(60,300);
		setVisible(true);
		setResizable(false);
		setTitle("D.E.M.I. - Programmer Calculator");
		addKeyListener(this);
		setFocusable(true);
		
		Calculator();	
	}
	
	public void Calculator() {	
		
		layout = new GridBagLayout();

		int p = 60; 
		
		layout.columnWidths = new int[] { p, p , p, p, p, p, p, p };
		layout.rowHeights = new int[] { p, p, p, p, p, p};
		panel = new JPanel(layout);
		
		/*************        Creating calculator GUI            *************/
		
		c = new GridBagConstraints();
		c.fill = GridBagConstraints.BOTH;
		c.gridwidth = 8;	
		screen = new JLabel("0");
		screen.setHorizontalAlignment(SwingConstants.RIGHT);
		screen.setBackground(screenColor);
		screen.setFont(screenFont);
		screen.setOpaque(true);
		panel.add(screen, c);
		
		c.gridwidth = 1;
		String actionName[] = createTable(0);
		
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 3; j++) {
				int k = i*3+j;
				actions[k] = new JButton(actionName[k]);
				actions[k].setOpaque(true);
				actions[k].setFont(buttonsFont);
				actions[k].addActionListener(this);
				actions[k].setBackground(buttonColor);
				c.gridx = j;
				c.gridy = i+1;
				panel.add(actions[k], c);
			}
		}
		
		String value[] = createTable(1);
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				int k = i*5+j;
				
				if (i == 4 && j > 1) {
					
					if (j == 2) {
						c.gridwidth = 2;
						buttonAdd(k, value[k], i+1, j+3);
					}
					else if (j == 4) {
						c.gridwidth = 1;
						buttonAdd(k-1, value[k-1], i+1, j+3);
					}
				}
				else {
					buttonAdd(k, value[k], i+1, j+3);
				}
				
			}
		}
		viewMode();
		sizeMode();
		add(panel);
		
		/********************************************************************/
		
		
	}
	
	String[] createTable(int action) {
		
	/***********      Method creating JButtons and it's names     ************/
		
		if (action == 0) {
			actions = new JButton[15];
			String table[] = { "QW", "Rol", "Ror", "DW", "Or", "Xor",
					"W", "Lsh", "Rsh", "Byte", "Not", "And",
					"Dec", "Hex", "Bin" };
			return table;
		}
		else if (action == 1) {
			values = new JButton[24];
			String table[] =  { "B", "A", "BC", "CE", "+/-",
					"C", "7", "8", "9", "/",
					"D", "4", "5", "6", "*",
					"E", "1", "2", "3", "-",
					"F", "0", "=", "+" };
			return table;
		}
		return null;
		
	/*************************************************************************/
		
	}
	
	void buttonAdd(int k, String name, int i, int j) {
		c.gridx = j;
		c.gridy = i;
		values[k] = new JButton(name);
		values[k].setOpaque(true);
		values[k].setFont(buttonsFont);
		values[k].addActionListener(this);
		values[k].setBackground(buttonColor);	
		panel.add(values[k], c);		
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		
		Object src = e.getSource();
		
		if (src == actions[0]) {  //QWord
			size = "qword";
			sizeMode();
			showOnScreen(binary, ptr);
		}
		else if (src == actions[3]) {  //DWord
			size = "dword";
			sizeMode();
			showOnScreen(binary, ptr);
		}
		else if (src == actions[6]) {  //Word
			size = "word";
			sizeMode();
			showOnScreen(binary, ptr);
		}
		else if (src == actions[9]) {  //Byte
			size = "byte";
			sizeMode();
			showOnScreen(binary, ptr);
		}
		else if (src == actions[7]) {  //Lsh
			binary = binaryOp.Lsh(binary, ptr+1, max);
			ptr--;
			showOnScreen(binary, ptr);
		}
		else if (src == actions[8]) {  //Rsh
			binary = binaryOp.Rsh(binary, ptr+1, max);
			ptr++;
			showOnScreen(binary, ptr);
		}
		else if (src == actions[4]) {  //Or
			operation = '+'; 
		}
		else if (src == actions[5]) {  //Xor
			operation = '|'; 
		}
		else if (src == actions[10]) {  //Not
			binary = binaryOp.Not(binary, max);
			whereIsPtr();
			showOnScreen(binary, ptr);
		}
		else if (src == actions[11]) {  //And
			operation = '&';
		}
		else if (src == actions[12]) {  //Dec
			view = "dec";
			viewMode();
			showOnScreen(binary, ptr);
		}
		else if (src == actions[13]) {  //Hex
			view = "hex";
			viewMode();
			showOnScreen(binary, ptr);
		}
		else if (src == actions[14]) {  //Bin
			view = "bin";
			viewMode();
			showOnScreen(binary, ptr);
		}
		else if (src == values[2]) {  //BC
			binary = binaryOp.Rsh(binary, ptr+1, max);
			ptr++;
			showOnScreen(binary, ptr);
		}
		else if (src == values[3]) {  //CE
			binary2 = arithmetic.clearBinary(binary2);
			binary = arithmetic.clearBinary(binary);
			ptr = max;   ptr2 = max;  operation = ' ';
			showOnScreen(binary, ptr);
		}
		else if (src == values[22]) {  //=
			if (operation == '+') {
				binary = arithmetic.addiction(binary, binary2, max);
			}
			else if (operation == '-') {
				binary = arithmetic.substraction(binary, binary2, max);
			}
			else if (operation == '&') {
				binary = binaryOp.And(binary, binary2, max);
			}
			else if (operation == '|') {
				binary = binaryOp.Xor(binary, binary2, max);
			}
			else if (operation == '*') {
				binary = arithmetic.multiplication(binary, binary2, ptr, ptr2);				
			}
			else if (operation == '/') {
				binary = arithmetic.division(binary, binary2, ptr, ptr2);				
			}
			whereIsPtr();
			binary2 = arithmetic.clearBinary(binary2);
			showOnScreen(binary, ptr);
			
		}
		else if (src == values[23]) {  //+
			operation = '+'; 	
			binary2 = arithmetic.clearBinary(binary2);
		}
		else if (src == values[19]) {  //-
			operation = '-'; 	
			binary2 = arithmetic.clearBinary(binary2);
		}
		else if (src == values[14]) {  //*
			operation = '*'; 	
			binary2 = arithmetic.clearBinary(binary2);
		}
		else if (src == values[9]) {  // /
			operation = '/'; 	
			binary2 = arithmetic.clearBinary(binary2);
		}
		else if (src == values[20]) {  //F
			convertToBin("1111");
		}
		else if (src == values[15]) {  //E
			convertToBin("1110");
		}
		else if (src == values[10]) {  //D
			convertToBin("1101");
		}
		else if (src == values[5]) {  //C
			convertToBin("1100");
		}
		else if (src == values[0]) {  //B
			convertToBin("1011");
		}
		else if (src == values[1]) {  //A
			convertToBin("1010");
		}
		else if (src == values[8]) {  //9
			convertToBin("1001");
		}
		else if (src == values[7]) {  //8
			convertToBin("1000");
		}
		else if (src == values[6]) {  //7
			convertToBin("0111");
		}
		else if (src == values[13]) {  //6
			convertToBin("0110");
		}
		else if (src == values[12]) {  //5
			convertToBin("0101");
		}
		else if (src == values[11]) {  //4
			convertToBin("0100");
		}
		else if (src == values[18]) {  //3
			convertToBin("0011");
		}
		else if (src == values[17]) {  //2
			convertToBin("0010");
		}
		else if (src == values[16]) {  //1
			convertToBin("0001");
		}		
		else if (src == values[21]) {  //0
			convertToBin("0000");
		}	
	}
	
	
	public void convertToBin(String base) {
		
		if (operation == ' ') {		
			if (ptr == max) 
				ptr -= 4;
			else 
				binary = moveLeft(0, binary);

			for (int i = max; i > max-4; i--) {
				char tmp = base.charAt(i-max+3);
				binary[i] = tmp;
			}	
			clearNulls();
			showOnScreen(binary, ptr);			
		}
		else {
			if (ptr2 == max) 
				ptr2 -= 4;
			else 
				binary2 = moveLeft(1, binary2);

			for (int i = max; i > max-4; i--) {
				char tmp = base.charAt(i-max+3);
				binary2[i] = tmp;
			}	
			clearNulls2();
			showOnScreen(binary2, ptr2);	
		}
	}
	
	public void clearNulls() {
		boolean notZero = false;
		
		for (int i = 0; i <= max; i++) 
			if (binary[i] != ' ') {
				if (binary[i] == '0' && notZero == false) {
					binary[i] = ' ';
					ptr++;
				}
				else { break; }		
			}
	}
	
	public void clearNulls2() {
		boolean notZero = false;
		
		for (int i = 0; i <= max; i++) 
			if (binary2[i] != ' ') {
				if (binary2[i] == '0' && notZero == false) {
					binary2[i] = ' ';
					ptr2++;
				}
				else { break; }		
			}
	}
	
	public char[] moveLeft(int val, char binary[]) {
		
	/********    Moving of val binary values in table    *********/
		if (val == 0) {
			int l = ptr+5;
			for (int i = ptr+1; i < binary.length; i++) {
				if ((i - 4) >= 0) {
					binary[i - 4] = binary[i];
					if (i < l) {
						ptr--;  
					}
				}
			}
		}
		else {
			int l = ptr2+5;
			for (int i = ptr2+1; i < binary.length; i++) {
				if ((i - 4) >= 0) {
					binary[i - 4] = binary[i];
					if (i < l) {
						ptr2--;  
					}
				}
			}
		}
				
		return binary;
			
	/*************************************************************/
			
	}
	
	public void showOnScreen(char binary[], int ptr) {
		
	/*****   Method showing on screen(Label) actual value   ******/
		

		screen.setText(binaryOp.showOnScreen(binary, ptr, view));

		
	/**************************************************************/
		
	}
	
	
	public void viewMode() {
		if (view.equals("bin")) {
			actions[12].setBackground(buttonColor);
			actions[13].setBackground(buttonColor);
			actions[14].setBackground(activeView);
		}
		else if (view.equals("hex")) {
			actions[12].setBackground(buttonColor);
			actions[13].setBackground(activeView);
			actions[14].setBackground(buttonColor);
		}
		else if (view.equals("dec")) {
			actions[12].setBackground(activeView);
			actions[13].setBackground(buttonColor);
			actions[14].setBackground(buttonColor);
		}		
	}
	
	public void sizeMode() {
		binary = null;  
		binary2 = null;
		
		if (size.equals("dword")) {
			actions[0].setBackground(buttonColor);
			actions[3].setBackground(activeSize);
			actions[6].setBackground(buttonColor);
			actions[9].setBackground(buttonColor);
			max = 31;
		}
		else if (size.equals("word")) {
			actions[0].setBackground(buttonColor);
			actions[3].setBackground(buttonColor);
			actions[6].setBackground(activeSize);
			actions[9].setBackground(buttonColor);
			max = 15;
		}
		else if (size.equals("byte")) {
			actions[0].setBackground(buttonColor);
			actions[3].setBackground(buttonColor);
			actions[6].setBackground(buttonColor);
			actions[9].setBackground(activeSize);
			max = 7;
		}
		else if (size.equals("qword")) {
			actions[0].setBackground(activeSize);
			actions[3].setBackground(buttonColor);
			actions[6].setBackground(buttonColor);
			actions[9].setBackground(buttonColor);
			max = 63;
		}
		
		ptr = max;
		ptr2 = max;
		
		binary = new char[max+1];
		binary2 = new char[max+1];	
		binary2 = arithmetic.clearBinary(binary2);
		binary = arithmetic.clearBinary(binary);	
		
	}
	
	public void whereIsPtr() {
		ptr = max;
		for (int i = 0; i <= max; i++) {
			if (binary[i] == '1') {
				ptr = i-1;
				break;
			}
		}
	}
	
	@Override
	public void returnDEMI(String text) {}

	@Override
	public void keyPressed(KeyEvent e) {
		
		if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {  
			binary = binaryOp.Rsh(binary, ptr+1, max);
			ptr++;
			showOnScreen(binary, ptr);
		}
		else if (e.getKeyCode() == KeyEvent.VK_EQUALS) { 
			if (operation == '+') {
				binary = arithmetic.addiction(binary, binary2, max);
			}
			else if (operation == '-') {
				binary = arithmetic.substraction(binary, binary2, max);
			}
			else if (operation == '&') {
				binary = binaryOp.And(binary, binary2, max);
			}
			else if (operation == '|') {
				binary = binaryOp.Xor(binary, binary2, max);
			}
			else if (operation == '*') {
				binary = arithmetic.multiplication(binary, binary2, ptr, ptr2);				
			}
			else if (operation == '/') {
				binary = arithmetic.division(binary, binary2, ptr, ptr2);				
			}
			whereIsPtr();
			binary2 = arithmetic.clearBinary(binary2);
			showOnScreen(binary, ptr);
			
		}
		else if (e.getKeyCode() == KeyEvent.VK_PLUS || e.getKeyCode() == KeyEvent.VK_ADD) { 
			operation = '+'; 	
			binary2 = arithmetic.clearBinary(binary2);
		}
		else if (e.getKeyCode() == KeyEvent.VK_MINUS || e.getKeyCode() == KeyEvent.VK_SUBTRACT) {
			operation = '-'; 	
			binary2 = arithmetic.clearBinary(binary2);
		}
		else if (e.getKeyCode() == KeyEvent.VK_DIVIDE) {
			operation = '/'; 	
			binary2 = arithmetic.clearBinary(binary2);
		}
		else if (e.getKeyCode() == KeyEvent.VK_F) { 
			convertToBin("1111");
		}
		else if (e.getKeyCode() == KeyEvent.VK_E) {  
			convertToBin("1110");
		}
		else if (e.getKeyCode() == KeyEvent.VK_D) {  
			convertToBin("1101");
		}
		else if (e.getKeyCode() == KeyEvent.VK_C) {  
			convertToBin("1100");
		}
		else if (e.getKeyCode() == KeyEvent.VK_B) { 
			convertToBin("1011");
		}
		else if (e.getKeyCode() == KeyEvent.VK_A) {  
			convertToBin("1010");
		}
		else if (e.getKeyCode() == KeyEvent.VK_9 || e.getKeyCode() == KeyEvent.VK_NUMPAD9) { 
			convertToBin("1001");
		}
		else if (e.getKeyCode() == KeyEvent.VK_8 || e.getKeyCode() == KeyEvent.VK_NUMPAD8) {  
			convertToBin("1000");
		}
		else if (e.getKeyCode() == KeyEvent.VK_7 || e.getKeyCode() == KeyEvent.VK_NUMPAD7) { 
			convertToBin("0111");
		}
		else if (e.getKeyCode() == KeyEvent.VK_6 || e.getKeyCode() == KeyEvent.VK_NUMPAD6) {  
			convertToBin("0110");
		}
		else if (e.getKeyCode() == KeyEvent.VK_5 || e.getKeyCode() == KeyEvent.VK_NUMPAD5) { 
			convertToBin("0101");
		}
		else if (e.getKeyCode() == KeyEvent.VK_4 || e.getKeyCode() == KeyEvent.VK_NUMPAD4) {  
			convertToBin("0100");
		}
		else if (e.getKeyCode() == KeyEvent.VK_3 || e.getKeyCode() == KeyEvent.VK_NUMPAD3) { 
			convertToBin("0011");
		}
		else if (e.getKeyCode() == KeyEvent.VK_2 || e.getKeyCode() == KeyEvent.VK_NUMPAD2) {  
			convertToBin("0010");
		}
		else if (e.getKeyCode() == KeyEvent.VK_1 || e.getKeyCode() == KeyEvent.VK_NUMPAD1) { 
			convertToBin("0001");
		}
		else if (e.getKeyCode() == KeyEvent.VK_0 || e.getKeyCode() == KeyEvent.VK_NUMPAD0) {  
			convertToBin("0000");
		}

	}

	@Override
	public void keyReleased(KeyEvent arg0) {}

	@Override
	public void keyTyped(KeyEvent arg0) {}

}
